package org.example.pro2_self_iocContainer.org.context;

import org.example.pro2_self_iocContainer.org.annotation.*;

import java.io.File;
import java.io.FileFilter;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

//spring基于注解配置的上下文类
public class YcAnnotationConfigApplicationContext implements YcApplicationContext {
    private Map<String, Object> beanMap = new ConcurrentHashMap<String, Object>();
    //                 bean// 存bean的实例
    private Map<String, YcBeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, YcBeanDefinition>();    //存bean的定义信息
//  bean ===》bean对应的信息    //存bean的定义信息

    /**
     * 构造方法
     *
     * @param componentClass :配置类
     */
    public YcAnnotationConfigApplicationContext(Class<?>... componentClass) {
        for (Class cls : componentClass) {
            //解析配置类中的@Bean注解
            parseBeanAnnotation(cls);
            //解析配置类中的 @ComponentScan注解中的value,得到要扫描的包.
            //待扫描的包的路径不为空，则递归扫描.
            if (cls.isAnnotationPresent(YcComponentScan.class)) {
                YcComponentScan ycComponentScan = (YcComponentScan) cls.getAnnotation(YcComponentScan.class);
                String[] basePackages = ycComponentScan.value();
                if (basePackages == null || basePackages.length <= 0) {
                    basePackages = ycComponentScan.basePackages();
                }
                if (basePackages == null || basePackages.length <= 0) {
                    basePackages = new String[1];
                    //当前这个配置类的路径做为扫描路径
                    basePackages[0] = cls.getPackage().getName();
                }
                for (String s : basePackages) {
                    System.out.println("待扫描的包的路径:" + s);
                }
                //递归扫描这个配置类中  basePackages指定的包及子包中的所有的待托管的类.
                recursiveLoadBeanDefinition(basePackages);
            }
            // 扫描包下的所有类
            //  解析配置类中的@Bean注解
            // 读取类的信息，创建BeanDefinition对象
        }
        try {
            // 再ioc
            createBean();
            //  再di
            doDi();
        }catch(Exception ex){
            ex.printStackTrace();
        }

    }


    @Override
    public Object getBean(String beanId) {
        YcBeanDefinition bd=this.beanDefinitionMap.get(beanId);
        if(  bd==null ){
            throw new RuntimeException("查无此bean");
        }
        String scope=bd.getScope();
        if(  "prototype".equalsIgnoreCase(    scope)){
            Object obj=null;
            try {
                obj = Class.forName(bd.getClassInfo()).newInstance();
                this.beanMap.put(beanId,obj);
                doDi();
                this.beanMap.remove(beanId);
                return obj;
            }catch(Exception ex){
                ex.printStackTrace();
                return null;
            }
        }
        if( beanMap.containsKey(   beanId)){
            return beanMap.get( beanId);
        }
        if(  bd.isLazy() ){
            Object obj=null;
            try {
                obj = Class.forName(bd.getClassInfo()).newInstance();
                beanMap.put(beanId, obj);
                doDi();
                return obj;
            }catch(Exception ex){
                ex.printStackTrace();
                return null;
            }
        }
        return null;
    }



    private String getBeanId(Class cls) {
        String beanId="";
        YcComponent ycComponent = (YcComponent) cls.getAnnotation(YcComponent.class);
        YcRepository ycRepository = (YcRepository) cls.getAnnotation(YcRepository.class);
        YcService ycService = (YcService) cls.getAnnotation(YcService.class);
        YcController ycController = (YcController) cls.getAnnotation(YcController.class);
        if(  ycComponent!=null  ){
            beanId=   ycComponent.value();
        }else if(   ycRepository!=null){
            beanId=ycRepository.value();
        }else if(  ycService!=null ){
            beanId=ycService.value();
        }else if(  ycController!=null ){
            beanId=ycController.value();
        }
        //如果按value()取不到 beanId名， 则按类名
        if( beanId==null || "".equalsIgnoreCase(beanId) ){
            String typename=cls.getSimpleName();
            beanId=typename.substring(0,1).toLowerCase()+ typename.substring(1);
        }
        return beanId;
    }



    private void doDi() throws IllegalAccessException, ClassNotFoundException, InstantiationException {
        //循环BeanDefinitionMap,创建 bean
        for (Map.Entry<String, Object> entry : this.beanMap.entrySet()) {
            //获取beanId
            String beanId = entry.getKey(); //Bean 的名称（如 "userDao"）
            //获取bd
            Object beanObject = entry.getValue();
            //查看这个对象的属性上是否有  di注解
            Field[] fields= beanObject.getClass().getDeclaredFields();
            for( Field f:fields){
                if(    f.isAnnotationPresent(  YcResource.class )  ){
                    //取出  YcResource注解 中的 value值，这个值是 beanId, 代表 要装配的bean的id
                    String toDiBeanId=f.getAnnotation(  YcResource.class).value();
                    Object obj=getToDiObject(   toDiBeanId );
                    //设置 此field为可访问 的
                    f.setAccessible(  true );
                    f.set(  beanObject,    obj  );  // this.userDao=userDao;
                }else if(  f.isAnnotationPresent(   YcAutowired.class)){
                    //TODO:　1. 取出属性的类型  2. 先从beanMap看是否有这种类型 的bean, 有，则返回，没有，则看beanDefinitionMap中是否这种类型 .
                    //1. 取出属性的类型
                    Class<?> fType=f.getType();
                    //2. 先从beanMap看是否有这种类型 的bean, 有，则返回，没有，则看beanDefinitionMap中是否这种类型.
                    for( Map.Entry<String,Object> entry1:this.beanMap.entrySet() ){
                        Object beanObj1=entry1.getValue();   //Bean 的实例对象（如 UserDaoImpl@0x1234）
                        if(  beanObj1.getClass().equals(  fType ) ){    //serDaoImpl@1234.getClass() == UserDao.class → false（UserDaoImpl 是实现类，不等于接口）
                            f.setAccessible(  true );    //// 允许访问私有字段
                            f.set(  beanObject,    beanObj1  );   // 注入找到的 Bean
                            break;
                        }
                    }
                    for( Map.Entry<String,YcBeanDefinition> entry1:this.beanDefinitionMap.entrySet() ){
                        YcBeanDefinition bd1=entry1.getValue();  //Bean 的定义信息（如 UserDao 的定义信息）
                        if(  bd1.getClassInfo().equals(  fType.getName() ) ){
                            f.setAccessible(  true );
                            // 如果类名匹配，用反射创建实例并注入
                            //  bd1.getClassInfo() 是类的全路径名，通过反射创建实例
                            //Class.forName()  通过类名加载类对象   再调用newInstance()创建实例
                            f.set(  beanObject,    Class.forName(  bd1.getClassInfo() ).newInstance()  );
                            break;
                        }
                    }
                    //如果没有找到， 则报错
                    if(  f.get(  beanObject )==null ){
                        throw new RuntimeException("Expected at least one  candidate bean but found none");
                    }
                }
            }
        }
    }

    private Object getToDiObject(String toDiBeanId) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        //如果beanMap中有，则直接返回
        if(  beanMap.containsKey(   toDiBeanId )){
            return beanMap.get(   toDiBeanId);
        }
        //判断beanDefinitionMap中是否有此BeanId, 如果有，还可以创建 ， 如果没有， 则报错
        if(   this.beanDefinitionMap.containsKey(   toDiBeanId)==false ){
            throw new RuntimeException("Expected at least one  candidate bean but found none");
        }
        YcBeanDefinition   bd= this.beanDefinitionMap.get(   toDiBeanId);
        if(   bd.isLazy()  ){
            String classpath=bd.getClassInfo();
            Object beanObj=Class.forName(   classpath).newInstance();
            beanMap.put( toDiBeanId,   beanObj);
            return beanObj;
        }
        if(    bd.getScope().equalsIgnoreCase("prototype")){
            String classpath=bd.getClassInfo();
            Object beanObj= Class.forName(    classpath ).newInstance();
            //因为prototype模式下， bean每次都要重新创建 . 所以没有存到beanMap中
            return beanObj;
        }
        return null;
    }

    /**
     * IOC
     */
    private void createBean() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        //循环BeanDefinitionMap,创建 bean
        for(    Map.Entry<String,YcBeanDefinition> entry: this.beanDefinitionMap.entrySet()  ){
            //获取beanId
            String beanId=entry.getKey();
            //获取bd
            YcBeanDefinition bd=entry.getValue();
            //判断bd中是否有lazy属性
            if(  !bd.isLazy() &&  !bd.getScope().equalsIgnoreCase("prototype")  ){
                //创建bean
                String classInfo= bd.getClassInfo();
                //创建 bean的方式有多种，这里只用到了通过无参构造 方法创建的方式 .
                Object beanObj= Class.forName(classInfo).newInstance();   //
                beanMap.put(beanId, beanObj);
            }
            //获取bd中classInfo
        }
    }

    /**
     * 递归 扫描  basePackages中的路径, 加载所有的托管类信息到 BeanDefinitionMap中
     * basePackages={"com.yc","org.springframework"}
     *
     * @param basePackages
     */
    private void recursiveLoadBeanDefinition(String[] basePackages) {
        //循环此数组
        for (String basePackageName : basePackages) {
            //加载包下.class文件  ->流   -> 文件路径  ->   com/yc/Person.class
            //替换包路径中的.为 /
            String packagePath = basePackageName.replaceAll("\\.", "/");
            try {
                //利用jvm加载 包中的class文件
                //线程上下文加载器.
                //                          取出当前线程       的   类加载器                packagePath路径下的资源
                //返回URL形式的集合形式
                Enumeration<URL> files = Thread.currentThread().getContextClassLoader().getResources(packagePath);
                while (files.hasMoreElements()) {
                    URL url = files.nextElement();
                    System.out.println("正在扫描的包下的文件路径为:" + url.getFile());  //多了一个  开头 /
                    //找描这个包下的所有的文件 ,筛选 出  .class的
                    findPackageClasses(url.getFile(), basePackageName);  // basePackageName是带.包名
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }

    /**
     * @param packagePath: 待扫描的包的绝对路径  :   /c:/xxxx/xxxx/com/yc/
     * @param packageName: com.yc   包路径 :       com.yc
     */
    private void findPackageClasses(String packagePath, String packageName)  {
        try {
            packagePath= URLDecoder.decode(   packagePath,"utf-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
        if (packagePath.startsWith("/")) {  //    /c:/xxx
            packagePath = packagePath.substring(1);
        }
        //取这个 packagePath路径下所有的文件 (包括子包)
        File file = new File(packagePath);
        //取所有的  .class的文件 或是目录
        File[] classFiles = file.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                if (pathname.getName().endsWith(".class") || pathname.isDirectory()) {
                    return true;
                } else {
                    return false;
                }
            }
        });
        System.out.println(classFiles);
        //循环  classFiles, 如果是 .class文件 ，则加载   如果是目录 ，则递归扫描
        if (classFiles != null && classFiles.length > 0) {
            for (File cf : classFiles) {
                if (cf.isDirectory()) {
                    //目录 ，则递归扫描
                    findPackageClasses(cf.getAbsolutePath(), packageName + "." + cf.getName());
                } else {
                    //文件 ，则jvm加载》
                    URLClassLoader uc = new URLClassLoader(new URL[]{});
                    try {
                        //               com.yc.di       .    order.class
                        Class cls = uc.loadClass(packageName + "." + cf.getName().replace(".class", ""));
                        //判断这个cls类上是否有  IOC注解 ，如果有，则创建 一个BeanDefinition对象，存到BeanDefinitionMap中
                        if (cls.isAnnotationPresent(YcComponent.class) ||
                                cls.isAnnotationPresent(YcRepository.class) ||
                                cls.isAnnotationPresent(YcService.class) ||
                                cls.isAnnotationPresent(YcController.class)
                        ) {
                            YcBeanDefinition bd = new YcBeanDefinition();
                            if (cls.isAnnotationPresent(YcLazy.class)) {
                                YcLazy lazy = (YcLazy) cls.getAnnotation(YcLazy.class);
                                boolean b = lazy.value();
                                bd.setLazy(b);
                            }
                            if (cls.isAnnotationPresent(YcScope.class)) {
                                YcScope scope = (YcScope) cls.getAnnotation(YcScope.class);
                                String s = scope.value();
                                bd.setScope(s);
                            }
                            bd.setClassInfo(packageName + "." + cf.getName().replace(".class", ""));
                            //获取beanId
                            String beanId = getBeanId(cls);
                            beanDefinitionMap.put(beanId, bd);

                        }
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                }
            }
        }
    }



    /**
     * 在cls配置类中查找@Bean修饰的方法， 托管对应的类的对象
     *
     * @param cls
     */
    private void parseBeanAnnotation(Class cls) {
        try {
            //将配置类交给spring托管.
            Object obj = cls.newInstance();
            String clzName = cls.getSimpleName();

            YcBeanDefinition bd = new YcBeanDefinition();
            bd.setLazy(false);
            bd.setScope("singleton");
            bd.setClassInfo(cls.getName());

            // 取出配置类的类名，首字母小写，作为beanId
            String beanId = clzName.substring(0, 1).toLowerCase() + clzName.substring(1);
            //将配置类手管到map中
            beanDefinitionMap.put(beanId, bd);
            beanMap.put(beanId, obj);

            //在这个配置类  cls中查找所有@YcBean注解 的方法， 解析它们，托管对应的类的对象
            Method[] ms = cls.getDeclaredMethods();
            //只取有@YcBean注解 的方法
            for (Method m : ms) {
                if (m.isAnnotationPresent(YcBean.class)) {
                    YcBean ycBean = m.getAnnotation(YcBean.class);
                    String ycBeanId = ycBean.value();
                    if (ycBeanId.equals("")) {
                        ycBeanId = m.getName();
                    }
                    //激活方法
                    Object o = m.invoke(obj, null);   // o即Apple对象
                    this.beanMap.put(ycBeanId, o);

                    YcBeanDefinition bd1 = new YcBeanDefinition();
                    bd1.setLazy(false);
                    bd1.setScope("singleton");
                    bd1.setClassInfo(o.getClass().getName());  // Apple类的全路径
                    this.beanDefinitionMap.put(ycBeanId, bd1);
                }
            }

            //  再ioc

            //  再di
        } catch (Exception ex) {
            ex.printStackTrace();
        }

    }


}
